/*
 * Created by SharpDevelop.
 * User: David
 * Date: 2006-11-27
 * Time: 00:36
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.Collections.Generic;

using tools;

using Labb4.Event;
using Labb4.Pooman.Misc;
using Labb4.Pooman.Game;

namespace Labb4.Media.Graphics
{
	/// <summary>
	/// Takes care of rendering things to the screen, fake double buffered
	/// 
	/// Implements the Singleton design pattern
	/// </summary>
	public sealed class RenderManager : Manager
	{
		#region members
		
		/// <summary>
		/// The one instance
		/// </summary>
		private static RenderManager		m_Instance = new RenderManager();
		/// <summary>
		/// Reference to the graphics of the screen drawing appears to be taking place on
		/// </summary>
		private System.Drawing.Graphics		m_Canvas;
		/// <summary>
		/// Reference to the double buffer back that drawing actually takes place on
		/// </summary>
		private BufferedGraphics			m_CanvasBuffer;
		/// <summary>
		/// Reference to the clock
		/// </summary>
		private TimeManager					m_Time;
		/// <summary>
		/// Reference ot the settings of the game
		/// </summary>
		private Properties					m_Settings;
		
		/// <summary>
		/// The number of times the draw method is called during one frame
		/// </summary>
		private int							m_BlitCount;
		
		#endregion
		
		#region constructors
		
		/// <summary>
		/// Constructor, initializes members
		/// </summary>
		private RenderManager() :
			base()
		{
			m_Canvas = null;
			m_CanvasBuffer = null;
			m_Time = TimeManager.Instance;
			m_Settings = Properties.Instance;
			m_BlitCount = 0;
		}
		
		#endregion
		
		#region methods
		
		/// <summary>
		/// This must be called before Initialize is called. The Update method won't work until first this
		/// method has been called with none null values, and then Initialize has been called.
		/// 
		/// If the screen is resized, these references will have to be reset as well.
		/// </summary>
		/// <param name="g">Reference to the graphics of the screen where drawing will appear to be drawn</param>
		/// <param name="b">Reference to the back buffer of the screen</param>
		public void SetGraphics(System.Drawing.Graphics g, BufferedGraphics b)
		{
			m_Canvas = g;
			m_CanvasBuffer = b;
		}
		
		/// <summary>
		/// Clears the screen and resets the blit count to 0
		/// </summary>
		public void CLS()
		{
			//actually this clears the screen of the back buffer, not the
			//visual screen. However, in line with the principle "code against
			//interface, not implementation, I leave this out in the comments,
			//since a user of this class really don't need to know about this.
			m_CanvasBuffer.Graphics.Clear(Color.Black);
			m_BlitCount = 0;
		}
		
		/// <summary>
		/// Draws a picture to the screen, transparency is not taken into concideration here.
		/// </summary>
		/// <param name="picture">The picture to draw</param>
		/// <param name="x">The x coordinate on the screen to draw it at</param>
		/// <param name="y">The y coordinate on the screen to draw it at</param>
		public void DrawPicture(Picture picture, float x, float y)
		{
			m_CanvasBuffer.Graphics.DrawImage(picture.ImageData, x, y);
			m_BlitCount++;
		}
		
		/// <summary>
		/// Draws a picture to the screen. If the pictures transparency key has been set, this
		/// will be used.
		/// </summary>
		/// <param name="picture">The picture to draw</param>
		/// <param name="destination">The destination rectangle on the screen, where the picture
		/// should be drawn</param>
		/// <param name="source">The source rectangle in the picture that should be drawn to the
		/// destination rectangle</param>
		public void DrawPicture(Picture picture, Rectangle destination, Rectangle source)
		{
			m_CanvasBuffer.Graphics.DrawImage(picture.ImageData, destination, (float)source.X, (float)source.Y, (float)source.Width, (float)source.Height, GraphicsUnit.Pixel, picture.Attributes);
			m_BlitCount++;
		}
		
		#endregion
		
		#region overrides
		
		/// <summary>
		/// Initializes the renderer, makes sure both buffers have been set before any rendereing can take place
		/// </summary>
		protected override void DoInitialize()
		{
			if((m_Canvas == null) || (m_CanvasBuffer == null))
			{
				MessageBox.Show("RenderManager.Initialize() must not be called before SetGraphics!");
				TearDown();
			}
		}
		
		/// <summary>
		/// This should be the final call of every frame.
		/// 
		/// If the game is in debug mode, some debug information is printed to the screen.
		/// 
		/// Copies the contents of the back buffer to the visual screen.
		/// </summary>
		/// <param name="deltaTime"></param>
		protected override void DoUpdate(ref int deltaTime)
		{
			if(m_Settings.Debug)
			{
				DebugInfo();
			}
			
			m_CanvasBuffer.Render(m_Canvas);
		}
		
		#endregion
		
		#region properties
		
		/// <summary>
		/// The one instance (get)
		/// </summary>
		public static RenderManager Instance
		{
			get
			{
				return m_Instance;
			}
		}
		
		#endregion
		
		#region private methods
		
		/// <summary>
		/// Draws some debug information to the screen
		/// </summary>
		private void DebugInfo()
		{
			Pen redPen = new Pen(Color.Red);
			System.Drawing.Font font = new System.Drawing.Font(System.Drawing.FontFamily.Families[0], 10.0f);
			
			string debugInfo1 = "Current time: ";
			string debugInfo2 = "This frames game delta time: ";
			string debugInfo3 = "Frame rate: ";
			string debugInfo4 = "Blit count: ";
			
			debugInfo1 += m_Time.CurrentGameTime.ToString();
			debugInfo2 += m_Time.GameDeltaTime.ToString();
			debugInfo3 += m_Time.FrameRate.ToString();
			debugInfo4 += m_BlitCount;
			
			m_CanvasBuffer.Graphics.DrawString(debugInfo1, font, redPen.Brush, 1.0f, m_Canvas.VisibleClipBounds.Height - 90.0f);
			m_CanvasBuffer.Graphics.DrawString(debugInfo2, font, redPen.Brush, 1.0f, m_Canvas.VisibleClipBounds.Height - 75.0f);
			m_CanvasBuffer.Graphics.DrawString(debugInfo3, font, redPen.Brush, 1.0f, m_Canvas.VisibleClipBounds.Height - 60.0f);
			m_CanvasBuffer.Graphics.DrawString(debugInfo4, font, redPen.Brush, 1.0f, m_Canvas.VisibleClipBounds.Height - 45.0f);
		}
		
		#endregion
	}
}
